/*
 * Settings.cpp
 *
 *  Created on: Jan 8, 2011
 *      Author: Kevin Mueller
 */

#include "Main.h"
#include "Settings.h"
#include "Exceptions.h"
#include "Helpers.h"
#include "PositioningSystem.h"

#include <QtCore>
#include <QFile>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QSettings>
#include <QGeoCoordinate>

Settings::Settings(QObject *parent) :
	QObject(parent) {
	settings[AUTOCENTER] = new AutoCenter(this);
	settings[LANGUAGE] = new Language(this);
	settings[ORIENTATION] = new DisplayOrientation(this);
	settings[UNBLANK] = new Unblank(this);
	settings[DEGREEFORMAT] = new DegreeFormat(this);
	settings[SPEEDLIMIT] = new SpeedLimit(this);
	settings[ANNOUNCEMENTS] = new Announcement(this);
	settings[ROUTECALCULATION] = new RouteCalculation(this);
	settings[UNIT] = new Unit(this);
	settings[COLORS] = new Colors(this);
	settings[ARRIVAL] = new Arrival(this);
	settings[GPSSTATUS] = new GPSStatus(this);
	settings[POLLING] = new PollingInterval(this);
	settings[REDUCEDPOLLING] = new ReducedPollingInterval(this);
	settings[SHOWCOMPASS] = new ShowCompass(this);
	settings[SHOWPOI] = new ShowPOI(this);
	settings[SHOWSCALE] = new ShowScale(this);
	settings[SHOWVELOCITY] = new ShowVelocityVector(this);
	settings[AVOIDHIGHWAYS] = new AvoidHighways(this);
	settings[AVOIDTOLLROADS] = new AvoidTollroads(this);
	settings[ZOOMLEVEL] = new ZoomLevel(this);
	settings[VOLUMEKEYS] = new VolumeKeys(this);
	settings[ROTATEMAP] = new RotateMap(this);
	settings[SHADOWMAPSIZE] = new ShadowMapSize(this);

	recentPlaces = new RecentPlaces(this);
	center = new CenterPosition();
	mapfileloc = new MapFileLocation();

	load();

	for (register int i = 0; i < NUM; i++) {
connect	(settings[i], SIGNAL(settingChanged(AbstractSetting*)), this, SLOT(changed()));
}
}

Settings::~Settings() {
	qDebug() << "Save settings";
	save();

	for (register int i = 0; i < NUM; i++) {
		delete settings[i];
	}
	delete recentPlaces;
	delete center;
	delete mapfileloc;
}

AbstractSetting* Settings::getAnnouncement() const {
	return settings[ANNOUNCEMENTS];
}

AbstractSetting* Settings::getArrival() const {
	return settings[ARRIVAL];
}

AbstractSetting* Settings::getAutoCenter() const {
	return settings[AUTOCENTER];
}

AbstractSetting* Settings::getAvoidHighways() const {
	return settings[AVOIDHIGHWAYS];
}

AbstractSetting* Settings::getAvoidTollroads() const {
	return settings[AVOIDTOLLROADS];
}

AbstractSetting* Settings::getColors() const {
	return settings[COLORS];
}

AbstractSetting* Settings::getDegreeFormat() const {
	return settings[DEGREEFORMAT];
}

AbstractSetting* Settings::getGPSStatus() const {
	return settings[GPSSTATUS];
}

AbstractSetting* Settings::getLanguage() const {
	return settings[LANGUAGE];
}

AbstractSetting* Settings::getOrientation() const {
	return settings[ORIENTATION];
}

AbstractSetting* Settings::getPollingInterval() const {
	return settings[POLLING];
}

AbstractSetting* Settings::getReducedPollingInterval() const {
	return settings[REDUCEDPOLLING];
}

AbstractSetting* Settings::getRotateMap() const {
	return settings[ROTATEMAP];
}

AbstractSetting* Settings::getRouteCalculation() const {
	return settings[ROUTECALCULATION];
}

AbstractSetting* Settings::getShadowMapSize() const {
	return settings[SHADOWMAPSIZE];
}

AbstractSetting* Settings::getShowCompass() const {
	return settings[SHOWCOMPASS];
}

AbstractSetting* Settings::getShowPOI() const {
	return settings[SHOWPOI];
}

AbstractSetting* Settings::getShowScale() const {
	return settings[SHOWSCALE];
}

AbstractSetting* Settings::getShowVelocityVector() const {
	return settings[SHOWVELOCITY];
}

AbstractSetting* Settings::getSpeedLimit() const {
	return settings[SPEEDLIMIT];
}

AbstractSetting* Settings::getUnblank() const {
	return settings[UNBLANK];
}

AbstractSetting* Settings::getUnit() const {
	return settings[UNIT];
}

AbstractSetting* Settings::getVolumeKeys() const {
	return settings[VOLUMEKEYS];
}

AbstractSetting* Settings::getZoomLevel() const {
	return settings[ZOOMLEVEL];
}

void Settings::changed() {
	emit settingsChanged();
}

RecentPlaces* Settings::getRecentPlaces() const {
	return recentPlaces;
}

CenterPosition* Settings::getCenterPosition() const {
	return center;
}

MapFileLocation* Settings::getMapFileLocation() const {
	return mapfileloc;
}

/*
 bool Settings::save() {
 QString filename = CONFIG_FILE;
 QFile file(filename);
 if (!file.open(QFile::WriteOnly | QFile::Text)) {
 qCritical() << "Error: Cannot write settings file "
 << qPrintable(filename) << ": "
 << qPrintable(file.errorString());
 return false;
 }

 QXmlStreamWriter xmlWriter(&file);
 xmlWriter.setAutoFormatting(true);
 xmlWriter.writeStartDocument();
 xmlWriter.writeStartElement("config");
 for (int i = 0; i < NUM; i++) {
 settings[i]->save(&xmlWriter);
 }
 xmlWriter.writeEndDocument();
 file.close();
 if(file.error()){
 qCritical() << "Error: Cannot write settings file "
 << qPrintable(filename) << ": "
 << qPrintable(file.errorString());
 return false;
 }
 return true;
 }
 */

bool Settings::save() const {
	QSettings set(QSettings::IniFormat, QSettings::UserScope,
			Main::ORGANIZATION_NAME(), Main::APPLICATION_NAME());

	set.beginGroup("MainApp");
	for (register int i = 0; i < NUM; i++) {
		settings[i]->save(set);
	}
	mapfileloc->save(set);
	set.endGroup();

	set.beginGroup("RecentPlaces");
	center->save(set);
	recentPlaces->save(set);
	set.endGroup();
	return true;
}

bool Settings::load() const {
	QSettings set(QSettings::IniFormat, QSettings::UserScope,
			Main::ORGANIZATION_NAME(), Main::APPLICATION_NAME());

	set.beginGroup("MainApp");
	for (register int i = 0; i < NUM; i++) {
		settings[i]->load(set);
	}
	mapfileloc->load(set);
	set.endGroup();

	set.beginGroup("RecentPlaces");
	center->load(set);
	recentPlaces->load(set);
	set.endGroup();
	return true;
}

/**********************************************************/

CenterPosition::CenterPosition() :
	QGeoCoordinate() {
}

void CenterPosition::setValue(QGeoCoordinate &c) {
	setLatitude(c.latitude());
	setLongitude(c.longitude());
}

void CenterPosition::save(QSettings& settings) const {
	settings.setValue(QString("center_latitude"), latitude());
	settings.setValue(QString("center_longitude"), longitude());
}

void CenterPosition::load(QSettings& settings) {
	double
			lat =
					settings.value(QString("center_latitude"), "51.34027778").toDouble();
	double
			lon =
					settings.value(QString("center_longitude"), "12.37472222").toDouble();

	setLatitude(lat);
	setLongitude(lon);
}


MapFileLocation::MapFileLocation(){
}

void MapFileLocation::set(QString &s) {
	file = s;
}

QString MapFileLocation::get() const{
	return file;
}

void MapFileLocation::save(QSettings& settings) const {
	settings.setValue(QString("mapfileloc"), file);
}

void MapFileLocation::load(QSettings& settings) {
	QString def = QString("/home/user/.config/%1/map");
	def = def.arg(Main::ORGANIZATION_NAME());
	file = settings.value(QString("mapfileloc"), def).toString();
}

RecentPlaces::RecentPlaces(QObject* parent) :
	QObject(parent) {
	iSize = 0;
	/*
	 for(int i=0;i<MAX;i++){
	 list[i] = 0;
	 }
	 */
}

RecentPlaces::~RecentPlaces() {

}

void RecentPlaces::add(const QString& label,
		const QtMobility::QGeoCoordinate& coordinate) {
	//	LabeledCoordinate* rp = new LabeledCoordinate(label, coordinate);
	//	add(rp);
	LabeledCoordinate tmp(label, coordinate);
	add(tmp);
}

void RecentPlaces::add(const LabeledCoordinate& place) {
	//	LabeledCoordinate* rp = new LabeledCoordinate(place);
	//	add(rp);
	register int start = MAX - 2;
	bool inserted = true;
	for (register int i = 0; i < MAX; i++) {
		if (list[i] == place) {
			start = i - 1;
			inserted = false;
			break;
		}
	}

	for (register int i = start; i >= 0; i--) {
		list[i + 1] = list[i];
	}
	list[0] = place;

	if (iSize < MAX && inserted)
		iSize++;
}

int RecentPlaces::size() const {
	return iSize;
}

LabeledCoordinate RecentPlaces::get(int i) const {
	if (i < 0 || i >= size()) {
		throw OutOfBoundsException();
	}
	return list[i];
}

/*
 void RecentPlaces::getAll(){
 return list;
 }
 */

void RecentPlaces::save(QSettings& settings) const {
	register const int s = size();
	for (register int i = 0; i < s; i++) {
		settings.setValue(QString("place_%1_label").arg(i), list[i].getLabel());
//		settings.setValue(QString("place_%1_altitude").arg(i),
//				list[i].altitude());
		settings.setValue(QString("place_%1_latitude").arg(i),
				list[i].latitude());
		settings.setValue(QString("place_%1_longitude").arg(i),
				list[i].longitude());
	}
}

void RecentPlaces::load(QSettings& settings) {
	iSize = MAX;
	for (register int i = 0; i < MAX; i++) {
		QString label = settings.value(QString("place_%1_label").arg(i), "").toString();

		if (label == "") {
			iSize = i;
			break;
		}

//		double alt = settings.value(QString("place_%1_altiude").arg(i), "").toDouble();
		double lat = settings.value(QString("place_%1_latitude").arg(i), "").toDouble();
		double lon = settings.value(QString("place_%1_longitude").arg(i), "").toDouble();
		QtMobility::QGeoCoordinate coordinate(lat, lon);

		//		list[i] = new LabeledCoordinate(label, coordinate);
		list[i].setLabel(label);
		list[i].setCoordinate(coordinate);
	}
}

QAbstractListModel* RecentPlaces::model(QObject* parent) {
	return new RecentPlacesModel(this, parent != 0 ? parent : this);
}

RecentPlaces::RecentPlacesModel::RecentPlacesModel(RecentPlaces* places,
		QObject* parent) :
	QAbstractListModel(parent) {
	_data = places;
}

int RecentPlaces::RecentPlacesModel::rowCount(const QModelIndex &parent) const {
	return _data->size();
}

RecentPlaces* RecentPlaces::RecentPlacesModel::data() const {
	return _data;
}

LabeledCoordinate RecentPlaces::RecentPlacesModel::get(int id) const{
	return _data->get(id);
}

QVariant RecentPlaces::RecentPlacesModel::data(const QModelIndex &index,
		int role) const {
	if (!index.isValid())
		return QVariant();

	if (index.row() >= _data->size())
		return QVariant();

	if (role == Qt::DisplayRole)
		return _data->get(index.row()).getLabel();
	else
		return QVariant();
}

QVariant RecentPlaces::RecentPlacesModel::headerData(int section,
		Qt::Orientation orientation, int role) const {
	if (role != Qt::DisplayRole)
		return QVariant();

	if (orientation == Qt::Horizontal)
		return QString("Column %1").arg(section);
	else
		return QString("Row %1").arg(section);
}

/**********************************************************/

AbstractSetting::AbstractSetting(QString name, int min, int cnt,
		QObject *parent) :
	QObject(parent) {
	minimum = min;
	count = cnt;
	ident = name;
}

int AbstractSetting::getMinimum() const{
	return minimum;
}
int AbstractSetting::getMaximum() const{
	return minimum + count;
}

int AbstractSetting::getCurrent() const{
	return current;
}

void AbstractSetting::save(QSettings& settings) const{
	settings.setValue(ident, getCurrent());
	//	writer->writeTextElement(ident, QString::number(getCurrent()));
}

void AbstractSetting::load(QSettings& settings) {
	current = settings.value(ident, defaultValue).toInt();
}

void AbstractSetting::setDefault(int c) {
	defaultValue = c;
	current = c;
}

void AbstractSetting::setCurrent(int c) {
	c = qBound(getMinimum(), c, getMaximum());

	current = c;

	emit settingChanged(this);
}

/**********************************************************/

const char* AutoCenter::getDescription(int i) {
	if (i < this->getMinimum() || i >= this->getMaximum())
		throw OutOfBoundsException();
	return values[i];
}

void AutoCenter::calcRepositioning(const ExtendedGeoPositionInfo *posinfo) {
	if(posinfo==NULL)
		return;

	switch (getCurrent()) {
	case DISABLED:
		return;
	case FASTER_5:
		if (posinfo->getVelocity() < 5)
			return;
	case FASTER_10:
		if (posinfo->getVelocity() < 10)
			return;
	case FASTER_15:
		if (posinfo->getVelocity() < 15)
			return;
	case EVERYTIME:
		emit updatePosition(posinfo->coordinate());
	}
}

/**********************************************************/

const char* SpeedLimit::getDescription(int i) {
	if (i < this->getMinimum() || i >= this->getMaximum())
		throw OutOfBoundsException();
	return values[i];
}

/**********************************************************/

const char* Language::getDescription(int i) {
	if (i < this->getMinimum() || i >= this->getMaximum())
		throw OutOfBoundsException();
	return values[i];
}

/**********************************************************/

const char* DisplayOrientation::getDescription(int i) {
	if (i < this->getMinimum() || i > this->getMaximum())
		throw OutOfBoundsException();
	return values[i];
}

/**********************************************************/

const char* Unblank::getDescription(int i) {
	if (i < this->getMinimum() || i >= this->getMaximum())
		throw OutOfBoundsException();
	return values[i];
}

/**********************************************************/

const char* Unit::getDescription(int i) {
	if (i < this->getMinimum() || i >= this->getMaximum())
		throw OutOfBoundsException();
	return values[i];
}

/**********************************************************/

const char* DegreeFormat::getDescription(int i) {
	if (i < this->getMinimum() || i >= this->getMaximum())
		throw OutOfBoundsException();
	return values[i];
}

/**********************************************************/

const char* Announcement::getDescription(int i) {
	if (i < this->getMinimum() || i > this->getMaximum())
		throw OutOfBoundsException();
	return values[i];
}

/**********************************************************/

const char* RouteCalculation::getDescription(int i) {
	if (i < this->getMinimum() || i >= this->getMaximum())
		throw OutOfBoundsException();
	return values[i];
}

/**********************************************************/

const char* Colors::getDescription(int i) {
	if (i < this->getMinimum() || i >= this->getMaximum())
		throw OutOfBoundsException();
	return values[i];
}

/**********************************************************/

const char* Arrival::getDescription(int i) {
	if (i < this->getMinimum() || i >= this->getMaximum())
		throw OutOfBoundsException();
	return values[i];
}

/**********************************************************/

const char* GPSStatus::getDescription(int i) {
	if (i < this->getMinimum() || i >= this->getMaximum())
		throw OutOfBoundsException();
	return values[i];
}

/**********************************************************/

const char* PollingInterval::getDescription(int i) {
	if (i < this->getMinimum() || i > this->getMaximum())
		throw OutOfBoundsException();
	//return QString::number(i).append(tr(" sec"));

	sprintf(buf, "%d sec", i);
	return buf;
}

/**********************************************************/

const char* ReducedPollingInterval::getDescription(int i) {
	if (i < this->getMinimum() || i > this->getMaximum())
		throw OutOfBoundsException();
	if (i == 0) {
		return QT_TRANSLATE_NOOP("AbstractSetting", "Disabled");
	}

	sprintf(buf, "%d sec", i);
	return buf;
}

/**********************************************************/

const char* ShowCompass::getDescription(int i) {
	if (i < this->getMinimum() || i >= this->getMaximum())
		throw OutOfBoundsException();
	if (i == 0) {
		return QT_TRANSLATE_NOOP("AbstractSetting", "Disabled");
	}
	return QT_TRANSLATE_NOOP("AbstractSetting", "Enabled");
}

/**********************************************************/

const char* ShowPOI::getDescription(int i) {
	if (i < this->getMinimum() || i > this->getMaximum())
		throw OutOfBoundsException();
	if (i == 0) {
		return QT_TRANSLATE_NOOP("AbstractSetting", "Disabled");
	}
	return QT_TRANSLATE_NOOP("AbstractSetting", "Enabled");
}

/**********************************************************/

const char* ShowScale::getDescription(int i) {
	if (i < this->getMinimum() || i > this->getMaximum())
		throw OutOfBoundsException();
	if (i == 0) {
		return QT_TRANSLATE_NOOP("AbstractSetting", "Disabled");
	}
	return QT_TRANSLATE_NOOP("AbstractSetting", "Enabled");
}

/**********************************************************/

const char* ShowVelocityVector::getDescription(int i) {
	if (i < this->getMinimum() || i > this->getMaximum())
		throw OutOfBoundsException();
	if (i == 0) {
		return QT_TRANSLATE_NOOP("AbstractSetting", "Disabled");
	}
	return QT_TRANSLATE_NOOP("AbstractSetting", "Enabled");
}

/**********************************************************/

const char* AvoidTollroads::getDescription(int i) {
	if (i < this->getMinimum() || i > this->getMaximum())
		throw OutOfBoundsException();
	if (i == 0) {
		return QT_TRANSLATE_NOOP("AbstractSetting", "Disabled");
	}
	return QT_TRANSLATE_NOOP("AbstractSetting", "Enabled");
}

/**********************************************************/

const char* AvoidHighways::getDescription(int i) {
	if (i < this->getMinimum() || i > this->getMaximum())
		throw OutOfBoundsException();
	if (i == 0) {
		return QT_TRANSLATE_NOOP("AbstractSetting", "Disabled");
	}
	return QT_TRANSLATE_NOOP("AbstractSetting", "Enabled");
}

/**********************************************************/

const char* VolumeKeys::getDescription(int i) {
	if (i < this->getMinimum() || i >= this->getMaximum())
		throw OutOfBoundsException();
	return values[i];
}

/**********************************************************/

const char* ZoomLevel::getDescription(int i) {
	if (i < this->getMinimum() || i >= this->getMaximum())
		throw OutOfBoundsException();
	sprintf(buf, "%d", i);
	return buf;
}

/**********************************************************/

const char* ShadowMapSize::getDescription(int i) {
	if (i < this->getMinimum() || i > this->getMaximum())
		throw OutOfBoundsException();
	return values[i];
}

/**********************************************************/

const char* RotateMap::getDescription(int i) {
	if (i < this->getMinimum() || i > this->getMaximum())
		throw OutOfBoundsException();
	if (i == 0) {
		return QT_TRANSLATE_NOOP("AbstractSetting", "Disabled");
	}
	return QT_TRANSLATE_NOOP("AbstractSetting", "Enabled");
}
